import {
  ScreenSpaceEventType,
  ScreenSpaceEventHandler,
  Viewer,
  Cartographic,
  Cartesian2,
  Cartesian3,
  Math,
} from "cesium"

type Events = Map<EventType, Function[]>

export type PositionedEvent = ScreenSpaceEventHandler.PositionedEvent & {
  worldPosition: Cartesian3
  lnglat: [number, number, number] | undefined
  features: any[]
}

export type EventType =
  | "click"
  | "dblclick"
  | "leftdown"
  | "leftup"
  | "mousemove"
  | "rightdown"
  | "rightup"
  | "rightclick"
  | "middledown"
  | "middleup"
  | "middleclick"
  | "wheel"
  | "pinchstart"
  | "pinchend"
  | "pinchmove"

//基于Cesium的ScreenSpaceEventHandler拓展，使其支持回调列表；
export class CesiumEventEmitter {
  constructor(public viewer: Viewer) {}
  private handler = new ScreenSpaceEventHandler(this.viewer.canvas)
  // 用于存储事件及其回调函数的映射
  private events: Events = new Map()

  /**
   * 绑定事件
   * @param eventName 事件名称
   * @param callback 回调函数
   */
  on(eventName: EventType, callback: (event: PositionedEvent) => void): void {
    // 如果事件不存在，初始化一个空数组
    if (!this.events.has(eventName)) {
      this.events.set(eventName, [])
      const pointEventTypes: EventType[] = [
        "click",
        "dblclick",
        "leftdown",
        "leftup",
        "rightclick",
        "rightdown",
        "rightup",
        "middleclick",
        "middledown",
        "middleup",
        "mousemove",
      ]
      if (pointEventTypes.includes(eventName)) {
        this.handler.setInputAction(
          (
            e:
              | ScreenSpaceEventHandler.PositionedEvent
              | ScreenSpaceEventHandler.MotionEvent,
          ) => {
            //生成参数
            let position: Cartesian2
            if ("position" in e) {
              position = e.position
            } else {
              position = e.endPosition
            }
            const { scene } = this.viewer

            //获取笛卡尔坐标、经纬度坐标
            let lnglat: [number, number, number] | undefined
            const caresian = scene.pickPosition(position)
            const carto =
              caresian &&
              Cartographic.fromCartesian(caresian, scene.globe.ellipsoid)
            if (carto)
              lnglat = [
                Math.toDegrees(carto.longitude),
                Math.toDegrees(carto.latitude),
                carto.height,
              ]

            //获取拾取要素
            const features = scene.drillPick(position)

            //调用callback
            const callbacks = this.events.get(eventName)

            //下一帧调用，以消除drillPick破坏深度缓冲对后续拾取的影响
            requestAnimationFrame(() => {
              callbacks?.forEach((callback) => {
                callback({
                  worldPosition: caresian,
                  lnglat,
                  features,
                  ...e,
                })
              })
            })
          },
          this.transformEventType(eventName),
        )
      } else {
        this.handler.setInputAction((...args: any[]) => {
          const callbacks = this.events.get(eventName)
          callbacks?.forEach((callback) => {
            callback(...args)
          })
        }, this.transformEventType(eventName))
      }
    }
    // 将回调函数添加到事件列表中
    this.events.get(eventName)!.push(callback)
  }

  /**
   * 取消绑定事件
   * @param eventName 事件名称
   * @param callback 要移除的回调函数（可选）
   */
  off(eventName: EventType, callback?: Function): void {
    if (!this.events.has(eventName)) return

    if (callback) {
      // 移除指定的回调函数
      const callbacks = this.events.get(eventName)!
      const index = callbacks.indexOf(callback)
      if (index !== -1) {
        callbacks.splice(index, 1)
      }
    } else {
      // 移除所有回调函数
      this.events.delete(eventName)
    }
  }

  /**
   * 清空所有事件
   */
  clear(): void {
    this.events.clear()
  }

  //事件类型映射
  transformEventType(eventType: EventType) {
    switch (eventType) {
      case "click":
        return ScreenSpaceEventType.LEFT_CLICK
      case "dblclick":
        return ScreenSpaceEventType.LEFT_DOUBLE_CLICK
      case "leftdown":
        return ScreenSpaceEventType.LEFT_DOWN
      case "leftup":
        return ScreenSpaceEventType.LEFT_UP
      case "middleclick":
        return ScreenSpaceEventType.MIDDLE_CLICK
      case "middledown":
        return ScreenSpaceEventType.MIDDLE_DOWN
      case "middleup":
        return ScreenSpaceEventType.MIDDLE_UP
      case "mousemove":
        return ScreenSpaceEventType.MOUSE_MOVE
      case "pinchend":
        return ScreenSpaceEventType.PINCH_END
      case "pinchmove":
        return ScreenSpaceEventType.PINCH_MOVE
      case "pinchstart":
        return ScreenSpaceEventType.PINCH_START
      case "rightclick":
        return ScreenSpaceEventType.RIGHT_CLICK
      case "rightdown":
        return ScreenSpaceEventType.RIGHT_DOWN
      case "rightup":
        return ScreenSpaceEventType.RIGHT_UP
      case "wheel":
        return ScreenSpaceEventType.WHEEL
    }
  }
}
